package org.ko.core.dao.jpa;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.ko.core.domain.support.P;
import org.ko.core.websupport.page.Page;
import org.ko.core.websupport.page.Paginable;
import org.springframework.util.Assert;

/**
 * Dao - 基类
 * 
 * @author ByTheWay
 * @version 1.0.0
 */
public abstract class BaseDaoRawImpl<T, ID extends Serializable> implements BaseDao<T, ID> {

	/** 实体类类型 */
	private Class<T> entityClass;

	@PersistenceContext
	private EntityManager entityManager;

	protected EntityManager em() {
		return this.entityManager;
	}
	
	public EntityManager getEm() {
		return em();
	}

	@SuppressWarnings("unchecked")
	public BaseDaoRawImpl() {
		Type type = getClass().getGenericSuperclass();
		Type[] parameterizedType = ((ParameterizedType) type).getActualTypeArguments();
		entityClass = (Class<T>) parameterizedType[0];
	}

	public T find(ID id) {
		if (id != null) {
			return em().find(entityClass, id);
		}
		return null;
	} 
	
	@SuppressWarnings("unchecked")
	public <X> List<X> findList(String hql, P... params) {
		return this.<X>query(hql, params).getResultList();
	}
	
	@SuppressWarnings("unchecked")
	public <X> List<X> findin(ID... ids) {
		StringBuilder sb =  new StringBuilder();
		if(null!=ids&&ids.length>0){
			sb.append(" where id in(");
			for (ID id : ids) {
				sb.append(String.valueOf(id)).append(",");
			}
			sb.deleteCharAt(sb.length()-1);
			sb.append(")");
		}
		return this.<X>query("from " + entityClass.getSimpleName()+sb.toString()).getResultList();
	}

	

	public <X> List<X> findList(P... params) {
		return findList("from "+entityClass.getSimpleName(),params);
	}
	

	public Paginable findPage(P... params) {
		return findPage("from "+entityClass.getSimpleName(),params);
	}

	public Paginable findPage(String hql,P ...params) {
		P p = retriveFirst(params);
//		Pageable pageable = p.getPageable();
		
		long total = count("select count(*) "+hql.substring(hql.indexOf("from")), p);
		
		if(total==0)
			return new Page();
		
		return null;
	}

	public void save(T entity) {
		Assert.notNull(entity);
		
		if(isManaged(entity))
			update(entity);
		else
			em().persist(entity);
	}

	public T update(T entity) {
		Assert.notNull(entity);
		return em().merge(entity);
	}

	public void delete(T entity) {
		if (entity != null) {
			em().remove(entity);
		}
	}
	

	public void delete(ID pk) {
		if (pk != null) {
			delete(find(pk));
		}
	}

	public void refresh(T entity) {
		Assert.notNull(entity);
		em().refresh(entity);
	}

//	@SuppressWarnings("unchecked")
//	public ID getIdentifier(T entity) {
//		Assert.notNull(entity);
//		return (ID) em().getEntityManagerFactory().getPersistenceUnitUtil().getIdentifier(entity);
//	}
	public int update(String hql,P ...params) {
		return query(hql,params).executeUpdate();
	}
	
	public boolean isManaged(T entity) {
		return em().contains(entity);
	}

//	public void detach(T entity) {
//		em().detach(entity);
//	}

	public void lock(T entity, LockModeType lockModeType) {
		if (entity != null && lockModeType != null) {
			em().lock(entity, lockModeType);
		}
	}

	public void clear() {
		em().clear();
	}

	public void flush() {
		em().flush();
	}
	
	
	
	
	
	@SuppressWarnings("unchecked")
	public <X> X getOne(String hql,P ...params){
		P p =  retriveFirst(params);
		Query q = em().createQuery(hql);
		fillParams(q, p);
		return (X) q.getSingleResult();
	}
	public long count(String hql,P ...params) {
		return getOne(hql, params);
	}
	/**
	 * 指定结果类型查询
	 * @param <X>
	 * @param hql
	 * @param first
	 * @param count
	 * @param params
	 * @param orders
	 * @param clazz
	 * @return
	 */
	protected <X> Query query(String hql,P ...params) {
		StringBuilder sb = new StringBuilder(hql);
		P p = retriveFirst(params);
		
//		List<Sort> orders = p.getOrders();
//		if(null!=orders&&!orders.isEmpty()){
//			int orderIndex = sb.toString().toLowerCase().indexOf("order by");
//			if(-1==orderIndex){
//				//has no 'order by' exp,then append one. else append a comma
//				sb.append(" order by ");
//			}else{
//				sb.append(" , ");
//			}
//			for (Sort o : orders) {
//				sb.append(o.getProperty()).append(" ").append(o.getDirection());
//			}
//			
//		}
		
		Query q = em().createQuery(sb.toString());
		if(null!=p.getFirst())
			q.setFirstResult(p.getFirst());
		if(null!=p.getCount())
			q.setMaxResults(p.getCount());
		
		fillParams(q, p);
		
		return q;
	}
	
	
	private void fillParams(Query q, P p){
		switch (p.getMapType()) {
		case map:
			Map<String, Object> params = p.getMapParams();
			if(null!=params&&!params.isEmpty()){
				for (String key : params.keySet()) {
					q.setParameter(key, params.get(key));
				}
			}
			break;
		case array:
			Object [] array = p.getArrayParams();
			if(null!=array&&0!=array.length){
				for (int i = 0; i < array.length; i++) {
					q.setParameter(i+1, array[i]);
				}
			}
			break;
		default:
			break;
		}
	}
	
	
	
	private P retriveFirst(P ...params){
		//accept only first P
		if(null!=params&&params.length==1){
			return params[0];
		}else
			return P.begin();
	}
}